#include "test_os_win.h"
#include <sea_common>

// -------------------------------------------------------------------------------------------------

using namespace sea;

int gl_rep = 1000;

// -------------------------------------------------------------------------------------------------

void wait_it( )
{ 
    for( int i = 0; i < 100000; i++ ) { } 
}

// -------------------------------------------------------------------------------------------------

int test_atomic( )
{
    class ThreadA : public OsThread
    {
    public: ThreadA( OsAtomic* aVar ) : m_var( aVar ) { }
        void exec( ) 
        { 
            for( int i = 0; i < gl_rep; i++ )
            {
                m_var->inc( ); 
                wait_it( );
            }
        }
    private: OsAtomic* m_var;
    };

    OsAtomic var;
    ThreadA a( &var );
    ThreadA b( &var );

    a.start( );
    b.start( );

    OsTime timer;
    timer.start( );
    a.wait( );
    b.wait( );

    return timer.elapsed( );
}

// -------------------------------------------------------------------------------------------------

int test_spinlock( )
{
    class ThreadA : public OsThread
    {
    public: ThreadA( int* aVar, OsSpinlock* aLock ) : m_var( aVar ), m_lock( aLock ) { }
        void exec( ) 
        { 
            for( int i = 0; i < gl_rep; i++ )
            {
                m_lock->enter( );
                (*m_var)++; 
                wait_it( );
                m_lock->leave( );
            }
        }
    private: 
        int* m_var;
        OsSpinlock* m_lock;
    };

    int var = 0;
    OsSpinlock lock;
    ThreadA a( &var, &lock );
    ThreadA b( &var, &lock );

    a.start( );
    b.start( );

    OsTime timer;
    timer.start( );
    a.wait( );
    b.wait( );

    return timer.elapsed( );
}

int test_mutex( )
{
    class ThreadA : public OsThread
    {
    public: ThreadA( int* aVar, OsMutex* aLock ) : m_var( aVar ), m_lock( aLock ) { }
        void exec( ) 
        { 
            for( int i = 0; i < gl_rep; i++ )
            {
                m_lock->enter( );
                (*m_var)++; 
                wait_it( );
                m_lock->leave( );
            }
        }
    private: 
        int* m_var;
        OsMutex* m_lock;
    };

    int var = 0;
    OsMutex lock;
    ThreadA a( &var, &lock );
    ThreadA b( &var, &lock );

    a.start( );
    b.start( );

    OsTime timer;
    timer.start( );
    a.wait( );
    b.wait( );

    return timer.elapsed( );
}

template< typename T >
int test_sem( )
{
    class ThreadA : public OsThread
    {
    public: ThreadA( int* aVar, T* aLock ) : m_var( aVar ), m_lock( aLock ) { }
        void exec( )
        { 
            for( int i = 0; i < gl_rep; i++ )
            {
                m_lock->wait( );
                (*m_var)++;
                wait_it( );
                m_lock->signal( );
            }
        }
    private: 
        int* m_var;
        T* m_lock;
    };

    int var = 0;
    T lock( 1 );
    ThreadA a( &var, &lock );
    ThreadA b( &var, &lock );

    a.start( );
    b.start( );

    OsTime timer;
    timer.start( );
    a.wait( );
    b.wait( );

    return timer.elapsed( );
}

// -------------------------------------------------------------------------------------------------

void test_signaling_objects( )
{
    const int rep = 1;
    {
        i::int64 final = 0 ;
        for( int i = 0; i < rep; i++ )
        {
            final += test_atomic( );
        }
        SEA_TRACE( "\n *** Atomic test:: Final time %d ms", int( final / rep ) );
    }

    {
        i::int64 final = 0 ;
        for( int i = 0; i < rep; i++ )
        {
            final += test_spinlock( );
        }
        SEA_TRACE( "\n *** Spinlock:: Final time %d ms", int( final / rep ) );
    }

    {
        i::int64 final = 0 ;
        for( int i = 0; i < rep; i++ )
        {
            final += test_mutex( );
        }
        SEA_TRACE( "\n *** Mutex:: Final time %d ms", int( final / rep ) );
    }

    {
        i::int64 final = 0 ;
        for( int i = 0; i < rep; i++ )
        {
            final += test_sem< OsFastSemaphore >( );
        }
        SEA_TRACE( "\n *** FAST Semaphore:: Final time %d ms", int( final / rep ) );
    }

    {
        i::int64 final = 0 ;
        for( int i = 0; i < rep; i++ )
        {
            final += test_sem< OsSemaphore >( );
        }
        SEA_TRACE( "\n *** Semaphore:: Final time %d ms", final / rep );
    }
}

// -------------------------------------------------------------------------------------------------

